/**
 * @file .
 * @author wangxiaowei0303@163.com
 */

#include <math.h>
#include <utility>

#include "../string_util.h"
#include "AdaptiveLDFilter.h"

/* this->noiseVar 呈现给用户的名称. */
#define NOISE_VAR "noise-var"

void 
AdaptiveLDFilter::help(std::string &buf) const
{
    buf.append(
        "自适应局部降噪滤波器;适用于随机分布的噪声\n"
        NOISE_VAR";噪声的方差;参见图像退化模型,这里指的是 n(x,y) 的方差\n"
    );
    Filter::help(buf);
}

void
AdaptiveLDFilter::set(const char *argname,const char *argval)
{
    if (strEqual(argname,NOISE_VAR)) {
        this->noiseVar = str2double(argval);
        if (this->noiseVar < 0) 
            handleInvalidArgVal(__PRETTY_FUNCTION__,argname,argval);
    } else {
        Filter::set(argname,argval);
    }
    return ;
}

const char*
AdaptiveLDFilter::get(const char *argname,std::string &buf) const
{
    size_t oldSize = buf.size();
    if (strEqual(argname,NOISE_VAR)) 
        addFormat(buf,"%f",this->noiseVar);
    else 
       return Filter::get(argname,buf);
    return buf.data() + oldSize;
}

void
AdaptiveLDFilter::getSupportedArgs(
    std::vector<std::string> &args) const
{
    args.push_back(NOISE_VAR);
    Filter::getSupportedArgs(args);
    return ;
}

/*
 * 求取区间 [begin,end) 中元素之和.
 * @tparam IterType 要求: *IterType 是 numeric 类型.还要具有以下操作:
 *                  * operator++();
 * @return 元素之和, double 类型.
 */
template <typename IterType>
static double 
sumOf(IterType begin,IterType end)
{
    double sum = 0.0;
    for(; begin != end; ++begin) 
        sum += *begin;
    return sum;
}   

/*
 * 求取区间 [begin,end) 中元素的平均值.
 * @tparam IterType 与 sumOf() 一样.
 * @return 平均值.
 */
template <typename IterType>
static double
meanOf(IterType begin,IterType end)
{
    double sum = sumOf(begin,end);
    return sum / std::distance(begin,end);
}

/*
 * 求取区间 [begin,end) 中元素的方差.
 * @tparam IterType 与 sumOf() 一样.
 */
template <typename IterType>
static double
varOf(IterType begin,IterType end)
{
    double mean = meanOf(begin,end);
    double varsum = 0.0;
    for (; begin != end; ++begin) {
        double t = *begin - mean;
        varsum += t * t;
    }
    return varsum / std::distance(begin,end);
}

double
AdaptiveLDFilter::operOnNeigh(const cv::Mat &neigh)
{
    /*
     * NOTE;感觉这里有不少可以优化的地方,如在 varOf() 中计算 meanOf() 时,就已经
     * 求出 std::distance(begin,end);而在 varOf() 后面又一次 std::distance().
     * 在计算 localVar 时,需要 localMean,因此又一次在 varOf() 里面计算了一次均值.
     */
//    double localMean = meanOf(neign.begin<uchar>(),neigh.end<uchar>());
//    double localVar = varOf(neigh.begin<uchar>(),neigh.end<uchar>());
    double elemSum = sumOf(neigh.begin<uchar>(),neigh.end<uchar>());/* 元素之和 */
    double neighMean = elemSum / neigh.total();
    double neighVar = 0;
    for (cv::MatConstIterator_<uchar> iter = neigh.begin<uchar>(),
         end = neigh.end<uchar>();
         iter != end; ++iter) 
    {
        double t = *iter - neighMean;
        neighVar += t * t;
    }
    neighVar /= neigh.total();

    if (this->noiseVar > neighVar)
        return neighMean;
    
    uchar gxy = neigh.at<uchar>((neigh.rows - 1) / 2,(neigh.cols - 1) / 2);
    return gxy - (noiseVar / neighVar) * (gxy - neighMean);
}

NOISE_ALGO_REGISTER("AdaptiveLDFilter",AdaptiveLDFilter);
